home *** CD-ROM | disk | FTP | other *** search
/ Giga Games 1 / Giga Games.iso / net / go / info / rating < prev    next >
Encoding:
Text File  |  1993-06-20  |  10.2 KB  |  284 lines

  1. -- From Wilfred.Hansen@LOAN1.SP.CS.CMU.EDU Fri Dec 18 08:27:40 1992
  2.  
  3. -- Pseudo-code for a ratings algorithm
  4.  
  5. -- This algorithm computes p.rating, the rating for each player, p,
  6. -- assuming that the player's initial rating, p.seed, is within a few 
  7. -- stones of the correct value.  
  8. -- The approach is to measure the likelihood of each player's rating 
  9. -- and the likelihood of the outcome of each game given the difference
  10. -- in the opponent's ratings.  Multiplying all these likelihoods gives 
  11. -- likelihood of the set of game outcomes for the given set of ratings.
  12. --
  13. -- Each iteration step adjusts each player's rating by +delta, -delta,
  14. -- or not at all, depending on which increases the likelihood of the
  15. -- outcomes for the player's games.  Then the likelihood for the entire
  16. -- collection of ratings and game outcomes is recomputed and the change 
  17. -- by the current delta is accepted if the likelihood increased.  The 
  18. -- iteration repeats with diminishing values of delta until the desired
  19. -- significance is achieved.
  20.  
  21. -- For general discussion of the statistics, see: 
  22. --    Elo, Arpad E.,  Ratings of Chessplayers, past and present.  
  23. --        NY: Arco Pub., 1986
  24. --    Joe, Harry, "Rating systems based on paired comparison models", 
  25. --        Statistics and Probability Letters v. 11, 1991 p. 343-347
  26. --    David, H. A., "The method of paired comparisons"
  27.  
  28.  
  29. -- In this algorithm, a difference of one in rating is intended to
  30. -- indicate a difference of one stone in strength.  There is no built-in
  31. -- normalization, so there is no way to know if a rating of 1.00
  32. -- corresponds to Shodan;  however, relative values should be a 
  33. -- fair approximation of relative strength.  
  34. -- 
  35. -- Strictly speaking, the algorithm has no notion of a difference of one 
  36. -- stone;  the ratings are based on the players announced ratings.  If 
  37. -- players adopt consistent ratings that differ by, say, half a stone,
  38. -- the computed ratings will be based on that measure.  
  39. --
  40. -- When entering rating values, dan values are entered as the corresponding 
  41. -- positive number and the kyu value k is entered as 1-k (so 1 kyu is zero, 
  42. -- 2 kyu is -1, and so on).
  43.  
  44. -- Two special data types, Rating and Likelihood, appear below.
  45. --     Both are implemented as floating values.  
  46. -- Rating is a player rating as defined just above.  
  47. -- Likelihood is similar to a probability.  It is a mapping of a rating 
  48. --    or game result into [0,1] such that higher values correspond
  49. --    to more likely ratings or results.
  50.  
  51. -- Here are parameters as used for the AGA rating system:
  52.  
  53. p.seed: Rating  -- Players's initial rating.  
  54.     The latest rating at which the player played in a tournament.
  55. p.sigma: Rating = 
  56.     .5 for players rated at least twice before
  57.     .8 for players rated once before
  58.     1.0 for previously unrated players 10 kyu and above
  59.     2.0 for others
  60. px_sigma: Rating = 1.04      -- see description of game_po, below
  61.  
  62. handicapeqv: Rating      -- Rating point equivalents of handicaps
  63.         -- Handicap is Nstones stones on board
  64.         -- and a komi of additional points for white
  65.         --   Nstones is 0 or 2...9
  66.         --   -20 <= komi <= 20
  67.     === if Nstones == 0  then  .5 - .1 * komi else Nstones - .1*komi
  68.  
  69. maxdelta: Rating = 4      -- In successive passes, adjust ratings by 
  70.         --  4.  2.  1.  0.5  0.25  0.125  0.0625  
  71.         --  0.03125  0.015625  0.0078125  0.00390625
  72.  
  73. EPSILON: Number = 1e-15   -- Very small, positive non-zero value
  74. BIG: Number = 4          -- number of standard deviations of ratings change
  75.               --   after which to use EPSILON as likelihood
  76.  
  77. -- information for each game
  78. --
  79. Game:::
  80.     handicapeqv: Rating    -- see above
  81.     po: Likelihood        -- likelihood of the outcome given the 
  82.                 -- current rating difference of the players
  83.     oldpo: Likelihood         -- po from prior iteration
  84.     white: Player        -- refers to player info for white player
  85.     black: Player        --     ditto for black player
  86.     whitewins: Boolean    -- TRUE if W wins 
  87.  
  88. -- information for each player
  89. --
  90. Player:::
  91.     rating: Rating    -- current rating estimate 
  92.     pr: Likelihood     -- what is the likelihood of the current rating?
  93.     oldpr: Likelihood    -- pr from prior iteration
  94.     seed: Rating    -- initial rating 
  95.     sigma: Rating    -- standard deviation of curve of likelihood of
  96.             -- ratings in the neighborhood of seed.
  97.     direction: {UP, DOWN, NONE}  -- direction to adust rating
  98.     -- also: a list of all games the player has played in
  99.  
  100.  
  101. -- "pr" is the likelihood that a given rating is correct
  102. -- "po" similarly the likelihood that a game outcome is correct
  103. -- Multiplying all pr and pr values computes the total likelihood,
  104. -- "pt", that is, the likelihood of all game outcomes given a 
  105. -- particular set of ratings for the players.
  106.  
  107.  
  108. -- player_pr(p: Player): Likelihood
  109. --     Compute the "prior distribution", pr, of the rating for player 'p'.
  110. --     Assume the "correct" rating is in a sample space normally
  111. --    distributed about p.seed with standard deviation p.sigma.
  112. --    Compute z as the corresponding value in a normal
  113. --    distribution with mean of 0 and standard deviation of 1.
  114. --    Then compute the likelihood that z is the correct rating
  115. --    by evaluating the normal(0,1) density function at z.
  116. --
  117. player_pr(p: Player): Likelihood ===
  118.     z = ((p.rating - p.seed) / p.sigma)
  119.     return   exp(-z*z/2) / sqrt(2*pi)    -- normal density function
  120.     -- exp(...)/sqrt(...) is the function for the normal curve,
  121.     -- that is, a probability density function with mean 0 and s.d. 1.
  122.  
  123.  
  124. -- game_po(g: Game): Likelihood
  125. --    Compute the likelihood, po, of the outcome of game 'g'.
  126. --    The likelihood that white wins is the value of the normal
  127. --    distribution function evaluated at the ratings difference, 
  128. --    as adjusted by the handicap stones and normalized by px_sigma.
  129. --    With px_sigma of 1.04:
  130. --        ratings        likelihood
  131. --        difference    white wins
  132. --            0            .5
  133. --            1            .83
  134. --            2            .97
  135. --    That is, if the handicap is two stones too low, white will
  136. --    win 97 games out of a hundred.
  137. --    The likelihood of a black win is (1 - likelihood white wins).
  138. --
  139. game_po(g: Game): Likelihood ===
  140.     rd = g.white.rating - g.black.rating - g.handicapeqv
  141.     p = .5 + erf((rd/px_sigma) / sqrt(2)) / 2
  142.     if g.whitewins return p else return 1-p
  143.     --
  144.     -- erf(x) = [2/sqrt(pi)] * integral from 0 to x of exp(-t*t) dt
  145.     -- Some Unix systems have erf.  In the expression given, it computes
  146.     -- the normal distribution function, that is, the integral of the 
  147.     -- normal density function from negative infinity
  148.     -- to rd/px_sigma, the value whose likelihood we want.
  149.  
  150.     -- Ralston, Anthony, A First Course in Numerical Analysis, 
  151.     -- McGraw-Hill (New York, 1965), p. 21:
  152.     -- "the normal distribution function corresponding to the 
  153.     -- normal density function with zero mean and variance n/12
  154.     -- is given by 0.5 + 0.5 * erf(sqrt(6/n)x)".  
  155.     -- In the algorithm, the variance is 1, so n = 12.
  156.  
  157.  
  158. -- propose_ratings(delta: Rating)
  159. --    For a ratings change of 'delta', adjust all players
  160. --    according to their directions and recompute po for each game.
  161. --
  162. propose_ratings(delta: Rating) === 
  163.     for each player, p
  164.         case p.direction
  165.             UP:      p.rating += delta
  166.             DOWN:      p.rating -= delta
  167.             NONE:
  168.         p.oldpr = p.pr
  169.         p.pr = player_pr(p.rating, p.seed)
  170.     for each game, g
  171.         g.oldpo = g.po
  172.         g.po = game_po(g)
  173.  
  174.  
  175. --revert_ratings(delta: Rating)
  176. --    For a ratings change of 'delta', revert to prior ratings.
  177. --
  178. revert_ratings(delta: Rating) === 
  179.     for each player, p
  180.         case p.direction
  181.             UP:      p.rating -= delta
  182.             DOWN:      p.rating += delta
  183.         p.pr = p.oldpr
  184.     for each game, g
  185.         g.po = g.oldpo
  186.  
  187.  
  188. -- compute_pt(): Likelihood
  189. --    Compute the likelihood of the combination of all current ratings
  190. --    and the outcome of all games.
  191. -- In practice, repeated multiplication of probabilities can lead to 
  192. -- floating underflow.  It is preferable to use the logarithms of these
  193. -- values and use addition instead of multiplication.  Indeed, the entire
  194. -- algorithm can be rewritten to utilize logarithms of Likelihood values.
  195. --
  196. compute_pt(): Likelihood === 
  197.     pt = 1
  198.     for each player, p
  199.         pt *= p.pr
  200.     for each game, g
  201.         pt *= g.po
  202.     return pt
  203.  
  204.  
  205. --compute_player_impact(p: Player, delta: Rating): Number
  206. --    Compute the ratio ptNew/pt, where 
  207. --        pt is total likelihood given the current assignment of ratings 
  208. --        and ptNew is the likelihood resulting from 
  209. --        changing the rating for player 'p' by 'delta'.
  210. --    If this value is greater than one, the new rating is more accurate.
  211. --    The tests against BIG are because after four or five sd's, 
  212. --    the estimated rating has zero likelihood.  Therefore we give
  213. --    the likelihood a little slope back toward the seed to
  214. --    minimize the possibility of a plateau in the search.
  215. --
  216. compute_player_impact(p: Player, delta: Rating): Number ===
  217.     r_save = p.rating
  218.     p.rating += delta    -- temporarily change rating
  219.  
  220.     before = abs(r_save - p.seed) / p.sigma
  221.     after = abs(p.rating - p.seed) / p.sigma
  222.     if before > BIG and after > BIG
  223.         if after < before
  224.             pfactor = 1 + EPSILON         -- slightly better
  225.         else
  226.             pfactor = 1 / (1 + EPSILON)   -- slightly worse
  227.     else if after > BIG
  228.         pfactor = EPSILON / p.pr    -- off the edge - very unlikely
  229.     else
  230.         pfactor = player_pr(p) / p.pr
  231.         for each game, g, that p has played
  232.             pfactor *= game_po(g) / g.po
  233.  
  234.     p.rating = r_save
  235.     return pfactor
  236.  
  237.  
  238. -- estimate_ratings(): Likelihood
  239. --    Estimate ratings simultaneously for all players and games.
  240. --    Returns pt, the likelihood of the outcome, given the new ratings.
  241. --
  242. estimate_ratings(): Likelihood ===
  243.  
  244.     -- set ratings to seed values 
  245.     for each player, p
  246.         p.rating = p.seed
  247.         p.direction = NONE
  248.  
  249.     -- calculate initial values of all pt components 
  250.     propose_ratings(0)        -- compute initial po and pr
  251.     best_pt = compute_pt()        -- compute resulting pt
  252.  
  253.     -- search for best new rating values 
  254.     delta = max_delta
  255.     while delta >= .002        -- ensure two decimal places of accuracy
  256.         for each player, p
  257.             -- decide whether rating should go up or down
  258.             chng_plus  = compute_player_impact(p,  delta)
  259.             chng_minus = compute_player_impact(p,  -delta)
  260.             if chng_plus < 1 and chng_minus < 1 then
  261.                 p.direction = NONE
  262.             else if chng_plus > chng_minus
  263.                 p.direction = UP
  264.             else -- chng_minus > chng_plus
  265.                 p.direction = DOWN
  266.  
  267.         -- change ratings and repeat with same or smaller delta
  268.         propose_ratings(delta)
  269.         new_pt = compute_pt()
  270.         if new_pt > best_pt + EPSILON
  271.             best_pt = new_pt
  272.             -- do next cycle with same delta
  273.         else if new_pt >= best_pt
  274.             -- pt is no worse, but not much better; decrease delta
  275.             delta /= 2
  276.         else 
  277.             -- pt is no better;  undo the change & decrease delta
  278.             revert_ratings(delta)
  279.             delta /= 2
  280.  
  281.     return pt
  282.  
  283.  
  284.